﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using static System.Int32;


namespace Als.Utils.String
{
    public static class RandomHelper
    {
        #region 生成一个0.0到1.0的随机小数
        /// <summary>
        /// 生成一个0.0到1.0的随机小数
        /// </summary>
        public static double GetRandomDouble()
        {
            var random = new Random();
            return random.NextDouble();
        }
        #endregion

        #region 生成一个指定范围的随机整数
        /// <summary>
        /// 生成一个指定范围的随机整数，该随机数范围包括最小值，但不包括最大值
        /// </summary>
        /// <param name="minNum">最小值</param>
        /// <param name="maxNum">最大值</param>
        public static int GetRandomInt(int minNum, int maxNum)
        {
            var random = new Random();
            return random.Next(minNum, maxNum);
        }
        #endregion

        #region 生成随机字符串

        /// <summary>
        /// 产生随机数字字符串
        /// </summary>
        /// <param name="num">字符个数</param>
        /// <returns></returns>
        public static string RandomStr(int num)
        {
            var returnCode = string.Empty;
            var random = new Random();
            for (var i = 0; i < num; i++)
            {
                var number = random.Next();
                var code = (char)('0' + (char)(number % 10));
                returnCode += code.ToString();
            }
            return returnCode;
        }

        /// <summary>
        /// 生成随机数字字符串
        /// </summary>
        /// <param name="length">生成长度</param>
        /// <param name="sleep">是否要在生成前将当前线程阻止以避免重复</param>
        /// <returns></returns>
        public static string GetRandomNumber(int length, bool sleep)
        {
            if (sleep)
                System.Threading.Thread.Sleep(3);
            var result = "";
            var random = new Random();
            for (var i = 0; i < length; i++)
            {
                result += random.Next(10).ToString();
            }
            return result;
        }

        /// <summary>
        /// 生成随机数字字符串
        /// </summary>
        /// <param name="length">生成长度</param>
        /// <returns></returns>
        public static string GetRandomNumber(int length)
        {
            return GetRandomNumber(length, false);
        }

        /// <summary>
        /// 生成随机纯字母字符串
        /// </summary>
        /// <param name="length">生成长度</param>
        /// <param name="sleep">是否要在生成前将当前线程阻止以避免重复</param>
        /// <returns></returns>
        public static string GetRandomPureChar(int length, bool sleep)
        {
            if (sleep) System.Threading.Thread.Sleep(3);
            var pattern = new[] { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z' };
            var result = "";
            var n = pattern.Length;
            Random random = new Random(~unchecked((int)DateTime.Now.Ticks));
            for (var i = 0; i < length; i++)
            {
                var rnd = random.Next(0, n);
                result += pattern[rnd];
            }
            return result;
        }

        /// <summary>
        /// 生成随机纯字母字符串
        /// </summary>
        /// <param name="length">生成长度</param>
        /// <returns></returns>
        public static string GetRandomPureChar(int length)
        {
            return GetRandomPureChar(length, false);
        }

        /// <summary>
        /// 生成随机数字字符串
        /// </summary>
        /// <param name="intNumberLength">数字长度</param>
        /// <param name="onlyNumber">是否只有数字</param>
        /// <returns></returns>
        public static string GetRandomNumberString(int intNumberLength, bool onlyNumber)
        {
            var random = new Random();
            return GetRandomNumberString(intNumberLength, onlyNumber, random);
        }

        /// <summary>
        /// 生成随机数字字符串
        /// </summary>
        /// <param name="intNumberLength">数字长度</param>
        /// <param name="onlyNumber">是否只有数字</param>
        /// <param name="random"></param>
        /// <returns></returns>
        public static string GetRandomNumberString(int intNumberLength, bool onlyNumber, Random random)
        {
            var strings = "123456789";
            if (!onlyNumber) strings += "abcdefghjkmnpqrstuvwxyz";
            var chars = strings.ToCharArray();
            var returnCode = string.Empty;
            for (var i = 0; i < intNumberLength; i++)
                returnCode += chars[random.Next(0, chars.Length)].ToString();
            return returnCode;
        }

        /// <summary>
        /// 生成随机字符串
        /// </summary> 
        /// <param name="length">字符串长度</param>
        /// <param name="seed">随机函数种子值</param>
        /// <returns>指定长度的随机字符串</returns>
        public static string GetRandomChars(int length, params int[] seed)
        {
            var strSep = ",";
            var chrSep = strSep.ToCharArray();
            //这里定义字符集
            const string strChar = "0,1,2,3,4,5,6,7,8,9,a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z"
                                   + ",A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,W,X,Y,Z";
            var aryChar = strChar.Split(chrSep, strChar.Length);
            var strRandom = string.Empty;
            Random rnd;
            if (seed != null && seed.Length > 0)
            {
                rnd = new Random(seed[0]);
            }
            else
            {
                rnd = new Random();
            }
            //生成随机字符串
            for (var i = 0; i < length; i++)
            {
                strRandom += aryChar[rnd.Next(aryChar.Length)];
            }
            return strRandom;
        }

        /// <summary>
        /// 随机生成不重复数字字符串 
        /// </summary>
        /// <param name="codeCount">随机数长度</param>
        /// <returns></returns>
        public static string GenerateCheckCodeNum(int codeCount)
        {
            var rep = 0;
            var str = string.Empty;
            var num2 = DateTime.Now.Ticks + rep;
            rep++;
            var random = new Random(((int)(((ulong)num2) & 0xffffffffL)) | ((int)(num2 >> rep)));
            for (var i = 0; i < codeCount; i++)
            {
                var num = random.Next();
                str = str + ((char)(0x30 + ((ushort)(num % 10))));
            }
            return str;
        }

        /// <summary>
        /// 随机生成字符串（数字和字母混和）
        /// </summary>
        /// <param name="codeCount">随机数长度</param>
        /// <returns></returns>
        public static string GenerateCheckCode(int codeCount)
        {
            var rep = 0;
            var str = string.Empty;
            var num2 = DateTime.Now.Ticks + rep;
            rep++;
            var random = new Random(((int)(((ulong)num2) & 0xffffffffL)) | ((int)(num2 >> rep)));
            for (var i = 0; i < codeCount; i++)
            {
                char ch;
                var num = random.Next();
                if ((num % 2) == 0)
                {
                    ch = (char)(0x30 + ((ushort)(num % 10)));
                }
                else
                {
                    ch = (char)(0x41 + ((ushort)(num % 0x1a)));
                }
                str = str + ch;
            }
            return str;
        }

        /// <summary>  
        /// 生成22位唯一的数字 并发可用  
        /// </summary>  
        /// <returns></returns>  
        public static string GenerateUniqueId()
        {
            System.Threading.Thread.Sleep(1); //保证yyyyMMddHHmmssffff唯一  
            var d = new Random(BitConverter.ToInt32(Guid.NewGuid().ToByteArray(), 0));
            var strUnique = DateTime.Now.ToString("yyyyMMddHHmmssffff") + d.Next(1000, 9999);
            return strUnique;
        }

        #endregion

        #region 生成长度为N的随机汉字

        /// <summary>
        /// 生成长度为N的随机汉字
        /// </summary>
        /// <param name="strlength"></param>
        /// <returns></returns>
        public static string GetRandomChinese(int strlength)
        {
            // 获取GB2312编码页（表） 
            var gb = Encoding.GetEncoding("gb2312");
            var bytes = CreateRegionCode(strlength);
            var sb = new StringBuilder();
            for (var i = 0; i < strlength; i++)
            {
                var temp = gb.GetString((byte[])Convert.ChangeType(bytes[i], typeof(byte[])));
                sb.Append(temp);
            }
            return sb.ToString();
        }

        /// <summary>
        /// 此函数在汉字编码范围内随机创建含两个元素的十六进制字节数组
        /// 每个字节数组代表一个汉字
        /// 并将四个字节数组存储在object数组中
        /// 参数：strlength，代表需要产生的汉字个数
        /// </summary>
        /// <param name="strlength"></param>
        /// <returns></returns>
        private static object[] CreateRegionCode(int strlength)
        {
            //定义一个字符串数组储存汉字编码的组成元素 
            var rBase = new System.String[16] { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f" };
            var rnd = new Random();
            //定义一个object数组用来 
            var bytes = new object[strlength];
            /**
             每循环一次产生一个含两个元素的十六进制字节数组，并将其放入bytes数组中 
             每个汉字有四个区位码组成 
             区位码第1位和区位码第2位作为字节数组第一个元素 
             区位码第3位和区位码第4位作为字节数组第二个元素 
            **/
            for (var i = 0; i < strlength; i++)
            {
                //区位码第1位 
                var r1 = rnd.Next(11, 14);
                var strR1 = rBase[r1].Trim();

                //区位码第2位 
                rnd = new Random(r1 * unchecked((int)DateTime.Now.Ticks) + i); // 更换随机数发生器的 种子避免产生重复值 
                var r2 = rnd.Next(0, r1 == 13 ? 7 : 16);
                var strR2 = rBase[r2].Trim();

                //区位码第3位 
                rnd = new Random(r2 * unchecked((int)DateTime.Now.Ticks) + i);
                var r3 = rnd.Next(10, 16);
                var strR3 = rBase[r3].Trim();

                //区位码第4位 
                rnd = new Random(r3 * unchecked((int)DateTime.Now.Ticks) + i);
                int r4;
                switch (r3)
                {
                    case 10:
                        r4 = rnd.Next(1, 16);
                        break;
                    case 15:
                        r4 = rnd.Next(0, 15);
                        break;
                    default:
                        r4 = rnd.Next(0, 16);
                        break;
                }
                string strR4 = rBase[r4].Trim();

                // 定义两个字节变量存储产生的随机汉字区位码 
                var byte1 = Convert.ToByte(strR1 + strR2, 16);
                var byte2 = Convert.ToByte(strR3 + strR4, 16);
                // 将两个字节变量存储在字节数组中 
                var strR = new byte[] { byte1, byte2 };

                // 将产生的一个汉字的字节数组放入object数组中 
                bytes.SetValue(strR, i);
            }

            return bytes;
        }

        #endregion

        #region 生成订单号

        /// <summary>
        /// 生成订单号，全站统一格式(年月日时分秒+4位随机数)
        /// </summary>
        /// <returns></returns>
        public static string GetOrderNo()
        {
            return DateTime.Now.ToString("yyyyMMddHHmmss") + GetRandomNumberString(4, true);
        }

        #endregion

        #region Guid

        /// <summary>
        /// 获取有序的唯一ID
        /// </summary>
        /// <returns></returns>
        public static Guid GenerateGuid(SequentialGuidType sequentialGuidType = SequentialGuidType.SequentialAtEnd)
        {
            return SequentialGuidGenerator.NewSequentialGuid(sequentialGuidType);
        }

        /// <summary>
        /// 根据枚举生成不同的有序GUID
        /// </summary>
        private static class SequentialGuidGenerator
        {
            private static readonly RNGCryptoServiceProvider Rng = new RNGCryptoServiceProvider();

            public static Guid NewSequentialGuid(SequentialGuidType guidType)
            {
                var randomBytes = new byte[10];
                Rng.GetBytes(randomBytes);

                var timestamp = DateTime.UtcNow.Ticks / 10000L;
                var timestampBytes = BitConverter.GetBytes(timestamp);

                if (BitConverter.IsLittleEndian)
                {
                    Array.Reverse(timestampBytes);
                }

                var guidBytes = new byte[16];

                switch (guidType)
                {
                    case SequentialGuidType.SequentialAsString:
                    case SequentialGuidType.SequentialAsBinary:
                        Buffer.BlockCopy(timestampBytes, 2, guidBytes, 0, 6);
                        Buffer.BlockCopy(randomBytes, 0, guidBytes, 6, 10);

                        // If formatting as a string, we have to reverse the order
                        // of the Data1 and Data2 blocks on little-endian systems.
                        if (guidType == SequentialGuidType.SequentialAsString && BitConverter.IsLittleEndian)
                        {
                            Array.Reverse(guidBytes, 0, 4);
                            Array.Reverse(guidBytes, 4, 2);
                        }
                        break;
                    case SequentialGuidType.SequentialAtEnd:
                        Buffer.BlockCopy(randomBytes, 0, guidBytes, 0, 10);
                        Buffer.BlockCopy(timestampBytes, 2, guidBytes, 10, 6);
                        break;
                    default:
                        throw new ArgumentOutOfRangeException(nameof(guidType), guidType, null);
                }

                return new Guid(guidBytes);
            }
        }

        /// <summary>
        /// 有序GUID的类型
        /// sqlServer用AtEnd
        /// mysql用AsString或者AsBinary
        /// oracle用AsBinary
        /// postgresql用AsString或者AsBinary
        /// </summary>
        public enum SequentialGuidType
        {
            SequentialAsString,
            SequentialAsBinary,
            SequentialAtEnd
        }

        #endregion

        #region 随机红包

        /// <summary>
        /// 红包算法
        /// </summary>
        /// <param name="remainMoney">要分配的总额度</param>
        /// <param name="remainCount">要分配的份数</param>
        /// <returns></returns>
        public static List<double> GetRedPacketMoneys(double remainMoney, int remainCount)
        {
            //人均最小金额  
            const double min = 0.1;
            if (remainMoney < remainCount * min)
                return null;

            var num = remainCount;
            var array = new List<double>();
            var r = new Random();
            for (var i = 0; i < num; i++)
            {
                if (remainCount == 1)
                {
                    remainCount--;
                    array.Add(Convert.ToDouble(remainMoney.ToString("0.00")));
                }
                else
                {
                    //(remainMoney - (remainCount - 1) * min)：保存剩余金额可以足够的去分配剩余的红包数量  
                    var max = (remainMoney - (remainCount - 1) * min) / remainCount * 2;
                    var money = r.NextDouble() * max;
                    money = Convert.ToDouble((money <= min ? min : money).ToString("0.00"));
                    remainCount--;
                    remainMoney -= money;
                    array.Add(money);
                }
            }
            //再次随机  
            return array.OrderBy(o => Guid.NewGuid()).ToList();
        }


        #endregion

        #region 数组进行随机排序
        /// <summary>
        /// 对一个数组进行随机排序
        /// </summary>
        /// <typeparam name="T">数组的类型</typeparam>
        /// <param name="arr">需要随机排序的数组</param>
        public static void GetRandomArray<T>(T[] arr)
        {
            //对数组进行随机排序的算法:随机选择两个位置，将两个位置上的值交换
            //交换的次数,这里使用数组的长度作为交换次数
            var count = arr.Length;
            //开始交换
            for (var i = 0; i < count; i++)
            {
                //生成两个随机数位置
                var targetIndex1 = GetRandomInt(0, arr.Length);
                var targetIndex2 = GetRandomInt(0, arr.Length);
                //交换两个随机数位置的值
                var temp = arr[targetIndex1];
                arr[targetIndex1] = arr[targetIndex2];
                arr[targetIndex2] = temp;
            }
        }
        #endregion

        #region 得到随机日期
        /// <summary>
        /// 得到随机日期
        /// </summary>
        /// <param name="time1">起始日期</param>
        /// <param name="time2">结束日期</param>
        /// <returns>间隔日期之间的 随机日期</returns>
        public static DateTime GetRandomTime(DateTime time1, DateTime time2)
        {
            var random = new Random();
            DateTime minTime;
            var maxTime = new DateTime();

            var ts = new TimeSpan(time1.Ticks - time2.Ticks);

            // 获取两个时间相隔的秒数
            var dTotalSecontds = ts.TotalSeconds;
            var iTotalSecontds = 0;

            if (dTotalSecontds > MaxValue)
            {
                iTotalSecontds = MaxValue;
            }
            else if (dTotalSecontds < MinValue)
            {
                iTotalSecontds = MinValue;
            }
            else
            {
                iTotalSecontds = (int)dTotalSecontds;
            }


            if (iTotalSecontds > 0)
            {
                minTime = time2;
                maxTime = time1;
            }
            else if (iTotalSecontds < 0)
            {
                minTime = time1;
                maxTime = time2;
            }
            else
            {
                return time1;
            }

            var maxValue = iTotalSecontds;

            if (iTotalSecontds <= MinValue)
                maxValue = MinValue + 1;

            var i = random.Next(Math.Abs(maxValue));

            return minTime.AddSeconds(i);
        }
        #endregion



    }
}
